import {HandlerManager} from "./manager/HandlerManager"
import {LogFactory} from "./support/log"
import {Scanner} from "./support/util/Scanner"
import HandlerExecutionChain from "./web/HandlerExecutionChain"
import {IncomingMessage, OutgoingMessage, ServerResponse} from "http"
import {createRequestFacade, HttpRequestFacade} from "./web/request/HttpRequestFacade"
import {HttpResponseFacade} from "./web/response/HttpResponseFacade"
import {FilterManager} from "./manager/FilterManager"
import * as path from "path"
import {StaticResourceManager} from "./manager/StaticResourceManager"
import {ReturnValueHandlerManager} from "./manager/ReturnValueHandlerManager"

class Dispatcher implements ResourceLoader {
  private logger = LogFactory.getLogger("Dispatcher")
  private readonly workdir: string = ""
  private handlerManager: NullAble<HandlerManager>
  private filterManager: NullAble<FilterManager>
  private staticResourceManager: NullAble<StaticResourceManager>
  private returnValueManager: NullAble<ReturnValueHandlerManager>

  doDispatch(serverRequest: IncomingMessage, serverResponse: ServerResponse) {
    const req = createRequestFacade(serverRequest)
    const res = new HttpResponseFacade(serverResponse, {
      forward: this.doDispatch.bind(this)
    })
    this.doDispatchInternal(req, res)
  }

  doDispatchInternal(req: HttpRequest, res: HttpResponse) {
    this.filterManager.doFilter(req, res, () => {
      const handler: HandlerExecutionChain = this.handlerManager.match(req)
      if (!handler) {
        res.error(404, "not found")
        return
      }
      try {
        const returnValue = handler.handle(req, res)
        if(returnValue) {
          this.handleReturnValue(req, res, returnValue)
        }
      } catch (e) {

        this.handleHandlerException(req, res, e)
      }
    })
  }

  constructor(workdir: string) {
    this.logger.info("workdir:", workdir)
    this.workdir = workdir
    this.prepareEnv(workdir)

  }

  prepareEnv(workdir: string) {
    const timeStart = +new Date()
    this.scan()
    this.handlerManager = new HandlerManager()
    this.filterManager = new FilterManager()
    this.staticResourceManager = new StaticResourceManager(workdir)
    this.returnValueManager = new ReturnValueHandlerManager()
    const timeEnd = +new Date()
    this.logger.info(`prepare dispatcher env end  ${timeEnd - timeStart}ms`)
  }

  scan(): void {
    // 扫描内置资源
    Scanner.scan(path.resolve(__dirname, "support", "components"), /.*\.ts/, {
      deep: true,
      handler: filename => {
        require(filename)
      }
    })

    // 扫描外部资源
    Scanner.scan(this.workdir, /.*\.ts/, {
      deep: true,
      handler: filename => {
        require(filename)
      }
    })
  }

  handleReturnValue(req: HttpRequest, res: HttpResponse, value: any) {
    this.returnValueManager.resolveReturnValue(req, res, value)
  }

  handleHandlerException(req: HttpRequest, res: HttpResponse, e: Error) {
    res.error(500, e.message)
  }

  handleFilterException(req: HttpRequest, res: HttpResponse, e: Error) {
    res.error(400, e.message)
  }

}

export default Dispatcher



